#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <functional>
#include <map>
#include <pthread.h>
#include "Fail.hpp"
#include "RingQueue.hpp"
#include "LockGuard.hpp"
#include "Thread.hpp"

static uint16_t default_port = 8080; //默认端口号

//using:给std::function<std::string(std::string)>起别名为func_t
using func_t = std::function<std::string(std::string)>;

class UdpServer
{
public:
    //默认构造
    UdpServer(uint16_t port = default_port)
        : _port(port)
    {
        pthread_mutex_init(&_mutex, nullptr); // 初始化锁

        //this指针不能显示传，直接bind
        p = new Thread(1, std::bind(&UdpServer::Recv, this));
        c = new Thread(2, std::bind(&UdpServer::Send, this)); 
    }

    //初始化服务器
    void Start()
    {
        //1.创建套接字
        _sock = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sock < 0)
        {
            perror("create socket fail");
            exit(SOCKET_FAIL);
        }
        std::cout << "create socket success, _sock: " << _sock << std::endl;

        //2.给服务器指明ip和port
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));

        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        //local.sin_addr.s_addr = inet_addr(_ip.c_str()); //inet_addr自动将ip从点分制转换为4字节整数,并且从主机序列转换为网络序列
        local.sin_addr.s_addr = INADDR_ANY; //云服务器不能给定ip地址, INADDR_ANY表示任意地址

        //绑定
        if(bind(_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            perror("bind fail");
            exit(BIND_FAIL);
        }
        std::cout << "bind success, _port: " << _port << std::endl;

        p->run();
        c->run();
    }

    //添加用户信息映射:ip:port -> struct sockaddr_in
    void AddUser(std::string& name, struct sockaddr_in& msg)
    {
        LockGuard Lockguard(&_mutex); //加锁
        _userTable[name] = msg;
    }

    //接收消息
    void Recv()
    {
        while(true)
        {
            char buff[2048];

            //用于接收向服务器发消息的客户端的信息
            struct sockaddr_in sender;
            socklen_t len = sizeof(sender);
            memset(&sender, 0, sizeof(sender));

            //接收消息
            //n表示实际接收到的字节数
            int n = recvfrom(_sock, buff, sizeof(buff) - 1, 0, (struct sockaddr*)&sender, &len);
            if(n > 0)
            {
                buff[n] = '\0';
            }
            else
            {
                continue;
            }

            //提取消息并做格式化输出
            std::string sender_ip = inet_ntoa(sender.sin_addr);
            uint16_t sender_port = ntohs(sender.sin_port);
            std::cout << sender_ip << ":" << sender_port << "# " << buff << std::endl;

            //添加用户信息, 用ip:port表示用户的struct sockaddr_in
            std::string name = sender_ip;
            name += ":";
            name += std::to_string(sender_port);
            AddUser(name, sender);

            //将接收到的消息，存入消息队列
            _ring.push(buff);

            //std::string task1_result = _task1(buff); //处理任务1

            //自拟业务需求--将接收到的消息发送回客户端
            // if(sendto(_sock, task1_result.c_str(), task1_result.size(), 0, (struct sockaddr*)&sender, sizeof(sender)) < 0)
            // {
            //     perror("sendto fail");
            //     exit(SENDTO_FAIL);
            // }
        }
    }

    //向 _userTable 内的用户广播消息
    void Send()
    {
        while(true)
        {   
            //读取消息
            std::string message;
            _ring.pop(message);

            std::vector<struct sockaddr_in> v; //存放用户的struct sockaddr_in
            {
                LockGuard Lockguard(&_mutex); //加锁
                for(auto& e : _userTable)
                {
                    v.push_back(e.second);
                }
            }

            //广播消息
            for(auto& e : v)
            {
                sendto(_sock, message.c_str(), message.size(), 0, (struct sockaddr*)&e, sizeof(e));
            }
        }
    }

    ~UdpServer()
    {
        pthread_mutex_destroy(&_mutex); //释放锁

        p->join();
        c->join();

        delete p;
        delete c;
    }

private: 
    int _sock; //套接字
    //func_t _task1;
    uint16_t _port; //端口
    std::map<std::string, struct sockaddr_in> _userTable; //存储参与聊天的用户信息
    RingQueue<std::string> _ring; //循环队列
    pthread_mutex_t _mutex; //锁
    Thread* p; //生产线程
    Thread* c; //消费线程
    //std::string _ip; //云服务器不需要给定ip地址
};